package com.ifp.opengate.boot.utils;

import java.io.File;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.ArrayList;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ifp.core.log.Trace;
import com.ifp.opengate.boot.bean.OpenGateRoute;
import com.ifp.opengate.boot.dao.BlackListServiceImpl;
import com.ifp.opengate.boot.dao.MonitorLogMapper;
import com.ifp.opengate.boot.entity.Blacklist;
import com.ifp.opengate.boot.entity.MonitorLog;
import com.ifp.opengate.boot.task.ConnectorCacheDataTask;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

/**
 * 项目名：ifp-gateway <br>
 * 包名：com.ifp.opengate.boot.utils <br>
 * 类名：MailServiceImpl <br>
 * 描述：
 *
 * @author liulx
 * @create 2021-08-16 10:12
 */
@Component
public class MailServiceUtils {

    @Autowired
    private JavaMailSender mailSender;

    @Value("${mail.fromMail.addr}")
    private String from;

    @Value("${opengate_alerting_info}")
    String alertingInfoKey;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private ConnectorCacheDataTask connectorCacheDataTask;

    @Autowired
    private MonitorLogMapper monitorLogMapper;

    @Autowired
    private BlackListServiceImpl blackListService;

    /**
     * 检查发送告警信息临时存储时间
     */
    protected static int STASH_TIME = 20;

    /**
     * 限流 key 名称
     */
    protected static String RATELIMIT_KEY = "Ratelimiting";

    /**
     * 熔断 key 名称
     */
    protected static String CIRCUIT_BREAKER_KEY = "CircuitBreaker";

    /**
     * 熔断告警时间 1 小时，同一个接口同一个人在 1 小时内不重复告警
     */
    protected static Integer CIRCUIT_BREAKER_ALERTING_TIME_KEY = 3600;

    /**
     * 已熔断告警 Key 前缀
     */
    protected static String ALERTING_CIRCUIT_PREFIX = "opengate_alerting_circuit_";

    /**
     * 已限流告警 Key 前缀
     */
    protected static String ALERTING_RATELIMIT_PREFIX = "opengate_alerting_ratelimit_";

    /**
     * 黑名单 Redis Key
     */
    protected static String OPENGATE_BLACK_LIST = "opengate_black_list";

    /**
     * 发送限流告警通知
     * 
     * @param route 路由对象
     * @param ip 限流 ip
     * @param channel 请求渠道
     */
    @Transactional(rollbackFor = Exception.class)
    public synchronized void sendMailForRateLimit(Route route, String ip, String channel) {
        if (route == null) {
            return;
        }

        OpenGateRoute openGateRoute = connectorCacheDataTask.getRouteOfMap().get(route.getId());
        String routeContent = openGateRoute.getRouteName() + " " + openGateRoute.getRoutePath();
        Object jsonStr = redisUtils.get(alertingInfoKey);
        if (jsonStr == null) {
            return;
        }
        JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
        JSONArray userArray = jsonObject.getJSONArray(RATELIMIT_KEY);
        if (userArray != null && userArray.size() > 0) {
            userArray.forEach(userObj -> {
                JSONObject user = JSONUtil.parseObj(userObj);
                String key = ALERTING_RATELIMIT_PREFIX + route.getId() + "_" + user.getStr("id");
                Object o = redisUtils.get(key);
                Trace.logInfo(Trace.MODULE_COMMON, "给 {} 发送限流警告通知", user);
                if (o == null) {
                    sendSimpleMail(user.getStr("user_email"), "网关限流提醒",
                        "网关限流提醒：" + ip + " 访问" + routeContent + "次数超限，已限制访问，如需解除限制，请前往中台内管处理。");
                    // 临时存储已发送人的记录
                    redisUtils.set(key, key, STASH_TIME);
                }
            });
        }
        String ipKey = ALERTING_RATELIMIT_PREFIX + "_" + ip;
        if (redisUtils.get(ipKey) != null) {
            return;
        }
        redisUtils.set(ipKey, ip, STASH_TIME);
        // 记录入库
        saveToDb(ip, channel, openGateRoute);
        // 同时更新 Redis 名单记录
        if (redisUtils.hasKey(OPENGATE_BLACK_LIST)) {
            redisUtils.lSet(OPENGATE_BLACK_LIST, ip);
        } else {
            redisUtils.lSet(OPENGATE_BLACK_LIST, new ArrayList<String>() {
                {
                    add(ip);
                }
            });
        }
    }

    /**
     * 添加黑名单以及告警记录到数据库
     * 
     * @param ip
     * @param channel
     */
    @Transactional(rollbackFor = SQLException.class)
    public void saveToDb(String ip, String channel, OpenGateRoute openGateRoute) {
        // 加入黑名单
        Blacklist blacklist = new Blacklist();
        blacklist.setValue(ip);
        blacklist.setType("2");
        blacklist.setStatus("0");
        blacklist.setChannel(channel);

        blacklist.setGmtCreate(LocalDateTime.now());
        blacklist.setCreateBy(0);
        blacklist.setGmtModified(LocalDateTime.now());
        blacklist.setModifiedBy(0);
        blackListService.saveOrUpdate(blacklist, new LambdaQueryWrapper<Blacklist>().eq(Blacklist::getStatus, "0"));
        // blacklistMapper.insert(blacklist);
        // 告警记录表
        MonitorLog monitorLog = new MonitorLog();
        monitorLog.setAlertingTypeId("2");
        monitorLog.setSystemName(openGateRoute.getSystemName());
        monitorLog.setSystemId(openGateRoute.getSystemId());
        monitorLog.setRouteName(openGateRoute.getRouteName());
        monitorLog.setAlertingDate(LocalDateTime.now());
        monitorLog.setRatelimiterType("0");
        monitorLog.setRatelimiterIp(ip);
        monitorLog.setChannel(channel);
        monitorLog.setGmtCreate(LocalDateTime.now());
        monitorLogMapper.insert(monitorLog);
    }

    /**
     * 发送熔断告警通知
     * 
     * @param route
     */
    public synchronized void sendMailForHystrix(Route route) {
        if (route == null) {
            return;
        }

        OpenGateRoute openGateRoute = connectorCacheDataTask.getRouteOfMap().get(route.getId());
        String routeContent = openGateRoute.getRouteName();
        Object jsonStr = redisUtils.get(alertingInfoKey);
        if (jsonStr == null) {
            return;
        }
        JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
        JSONArray userArray = jsonObject.getJSONArray(CIRCUIT_BREAKER_KEY);
        if (userArray != null && userArray.size() > 0) {
            userArray.forEach(userObj -> {
                JSONObject user = JSONUtil.parseObj(userObj);
                String key = ALERTING_CIRCUIT_PREFIX + route.getId() + "_" + user.getStr("id");
                Object o = redisUtils.get(key);
                if (o == null) {
                    Trace.logInfo(Trace.MODULE_COMMON, "给 {} 发送熔断警告通知", user);
                    sendSimpleMail(user.getStr("user_email"), "网关熔断提醒", "网关熔断提醒：" + routeContent + " 已熔断，请尽快排查原因。");
                    redisUtils.set(key, key, CIRCUIT_BREAKER_ALERTING_TIME_KEY);
                } else {
                    Trace.logInfo(Trace.MODULE_COMMON, "已经给 {} 发送熔断警告通知", user);
                }
            });
        }

    }

    /**
     * 发送文本邮件
     * 
     * @param to
     * @param subject
     * @param content
     */
    public void sendSimpleMail(String to, String subject, String content) {
        if (StringUtils.isBlank(to)) {
            return;
        }
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(from);
        message.setTo(to);
        message.setSubject(subject);
        message.setText(content);

        try {
            mailSender.send(message);
            Trace.logInfo(Trace.MODULE_COMMON, "简单邮件已经发送：{}", message.toString());
        } catch (Exception e) {
            Trace.logError(Trace.MODULE_COMMON, "发送简单邮件时发生异常：{}", e);
        }

    }

    /**
     * 发送html邮件
     * 
     * @param to
     * @param subject
     * @param content
     */
    public void sendHtmlMail(String to, String subject, String content) {
        if (StringUtils.isBlank(to)) {
            return;
        }
        MimeMessage message = mailSender.createMimeMessage();

        try {
            // true表示需要创建一个multipart message
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);

            mailSender.send(message);
            Trace.logInfo(Trace.MODULE_COMMON, "html 邮件已经发送：{}", message.toString());
        } catch (MessagingException e) {
            Trace.logError(Trace.MODULE_COMMON, "发送 html 邮件时发生异常：{}", e);
        }
    }

    /**
     * 发送带附件的邮件
     * 
     * @param to
     * @param subject
     * @param content
     * @param filePath
     */
    public void sendAttachmentsMail(String to, String subject, String content, String filePath) {
        if (StringUtils.isBlank(to)) {
            return;
        }
        MimeMessage message = mailSender.createMimeMessage();

        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);

            FileSystemResource file = new FileSystemResource(new File(filePath));
            String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
            helper.addAttachment(fileName, file);
            // helper.addAttachment("test"+fileName, file);

            mailSender.send(message);
            Trace.logInfo(Trace.MODULE_COMMON, "带附件的邮件已经发送：{}", message.toString());
        } catch (MessagingException e) {
            Trace.logError(Trace.MODULE_COMMON, "发送带附件的邮件时发生异常：{}", e);
        }
    }

    /**
     * 发送正文中有静态资源（图片）的邮件
     * 
     * @param to
     * @param subject
     * @param content
     * @param rscPath
     * @param rscId
     */
    public void sendInlineResourceMail(String to, String subject, String content, String rscPath, String rscId) {
        if (StringUtils.isBlank(to)) {
            return;
        }
        MimeMessage message = mailSender.createMimeMessage();

        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);

            FileSystemResource res = new FileSystemResource(new File(rscPath));
            helper.addInline(rscId, res);

            mailSender.send(message);
            Trace.logInfo(Trace.MODULE_COMMON, "嵌入静态资源的邮件已经发送：{}", message.toString());
        } catch (MessagingException e) {
            Trace.logError(Trace.MODULE_COMMON, "发送嵌入静态资源的邮件时发生异常：{}", e);
        }
    }

}
